/*//////////////////////////////////////////////////////////////////////////////
//
//                  INTEL CORPORATION PROPRIETARY INFORMATION
//     This software is supplied under the terms of a license agreement or
//     nondisclosure agreement with Intel Corporation and may not be copied
//     or disclosed except in accordance with the terms of that agreement.
//          Copyright(c) 2002-2008 Intel Corporation. All Rights Reserved.
//
*/

#include "umc_defs.h"
#if defined (UMC_ENABLE_MP3_INT_AUDIO_ENCODER)

#include "umc_mp3_enc_int.h"
#include "umc_mp3_encoder_params.h"
#include "mp3enc.h"

namespace UMC {
  AudioCodec *CreateMP3EncoderInt() { return (new MP3EncoderInt); }

  MP3EncoderInt::MP3EncoderInt()
  {
    state = NULL;
    m_freq = 0;
    m_bitrate = 0;
    m_stereo = 0;
    stateMemId = 0;
    m_initialized = 0;
  }

  MP3EncoderInt::~MP3EncoderInt()
  {
    Close();
  }

  IppBool MP3EncoderInt::CheckBitRate(Ipp32s sample_rate,
                                      Ipp32s layer,
                                      Ipp32s bitrate,
                                      Ipp32s stereo)
  {
    Ipp32s ind;
    Ipp32s id;

    if (sample_rate == 44100 || sample_rate == 48000 || sample_rate == 32000)
      id = 1;
    else if (sample_rate == 22050 || sample_rate == 24000 || sample_rate == 16000)
      id = 0;
    else return ippFalse;

    if (id == 0 || layer != 3)
      return ippFalse;

    if (mp3enc_checkBitRate(id, layer, stereo, bitrate / 1000, &ind))
      return ippTrue;

    return ippFalse;
  }

  Status MP3EncoderInt::Init(BaseCodecParams *init)
  {
    Ipp32s st, br, fr, size;
    Ipp32s stereo_mode;
    MP3Status res;
    MP3EncoderParams* pMP3EncoderParams;

    // checks or create memory allocator;
    if (BaseCodec::Init(init) != UMC_OK) {
      vm_debug_trace(VM_DEBUG_ERROR, VM_STRING("Failed to create allocator!\n"));
      return UMC_ERR_ALLOC;
    }

    AudioCodecParams* pAudioCodecInit =
      DynamicCast<AudioCodecParams, BaseCodecParams>(init);

    if (!pAudioCodecInit)
      return UMC_ERR_NULL_PTR;

    fr = pAudioCodecInit->m_info_in.sample_frequency;
    st = pAudioCodecInit->m_info_in.channels;
    br = pAudioCodecInit->m_info_out.bitrate / 1000;

    m_frame_num = 0;
    m_pts_prev = 0;

    pMP3EncoderParams = DynamicCast<MP3EncoderParams, BaseCodecParams>(init);
    if (pMP3EncoderParams) {
      m_stereo_mode = pMP3EncoderParams->stereo_mode;
      m_ns_mode = pMP3EncoderParams->ns_mode;
      m_layer = pMP3EncoderParams->layer;
      m_br_mode = pMP3EncoderParams->mode;
    } else {
      m_stereo_mode = UMC_MPA_LR_STEREO;
      m_ns_mode = 0;
      m_layer = 3;
      m_br_mode = MPAENC_CBR;
    }

    if (st == 1)
      m_stereo_mode = UMC_MPA_MONO;

    switch (m_stereo_mode) {
      case UMC_MPA_MONO:
        stereo_mode = MPA_MONO; break;
      case UMC_MPA_LR_STEREO:
        stereo_mode = MPA_LR_STEREO; break;
      case UMC_MPA_MS_STEREO:
        stereo_mode = MPA_MS_STEREO; break;
      case UMC_MPA_JOINT_STEREO:
        stereo_mode = MPA_JOINT_STEREO; break;
      default:
        stereo_mode = MPA_LR_STEREO; break;
    };

    res = mp3iencInit(NULL, fr, st, m_layer, br, m_br_mode, stereo_mode, m_ns_mode, &size);
    if (res != MP3_OK)
      return StatusMP3_2_UMC(res);

    if (m_pMemoryAllocator->Alloc(&stateMemId, size,
      UMC_ALLOC_PERSISTENT) != UMC_OK) {
        vm_debug_trace(VM_DEBUG_ERROR, VM_STRING("External allocation failed\n"));
        return UMC_ERR_ALLOC;
      }

    state = (MP3Enc *)m_pMemoryAllocator->Lock(stateMemId);
    if(!state) {
      vm_debug_trace(VM_DEBUG_ERROR, VM_STRING("External Lock failed\n"));
      return UMC_ERR_ALLOC;
    }

    res = mp3iencInit(state, fr, st, m_layer, br, m_br_mode, stereo_mode, m_ns_mode, &size);

    if (res == MP3_OK) {
        m_stereo = st;
        m_freq = fr;
        m_bitrate = br;
        m_initialized = 1;
    }

    MemUnlock();
    return StatusMP3_2_UMC(res);
  }

  Status MP3EncoderInt::Close()
  {
    Status res = MemLock();
    if (res != UMC_OK) {
      vm_debug_trace(VM_DEBUG_ERROR, VM_STRING("MemLock failed\n"));
      return UMC_OK;
    }
    mp3iencClose(state);
    MemUnlock();

    if(state) {
      m_pMemoryAllocator->Free(stateMemId);
      state = NULL;
    }

    BaseCodec::Close();

    return UMC_OK;
  }

  Status  MP3EncoderInt::GetFrame(MediaData *in, MediaData *out) {
    Ipp32s inSamples;
    Ipp32s bytes;
    Ipp32s out_size, slot_size;
    MP3Status res;
    Ipp64f pts_start, pts_end;

    if (!in || !in->GetDataPointer() || !out || !out->GetDataPointer())
      return UMC_ERR_NULL_PTR;

    if (!m_initialized)
      return UMC_ERR_NOT_INITIALIZED;

    inSamples = (in->GetDataSize() / sizeof(Ipp16s));

    if (inSamples < m_stereo * 1152)
      return UMC_ERR_NOT_ENOUGH_DATA;

    out_size = (out->GetBufferSize() - ((Ipp8u*)out->GetDataPointer() -
      (Ipp8u*)out->GetBufferPointer()));

    Status status = MemLock();
    if (status != UMC_OK) {
      vm_debug_trace(VM_DEBUG_ERROR, VM_STRING("MemLock failed\n"));
      return status;
    }

    mp3iencGetSlotSize(state, &slot_size);
    if (out_size < slot_size + (m_layer == 3 ? 1024 : 0)) {
      MemUnlock();
      return UMC_ERR_NOT_ENOUGH_BUFFER;
    }

    if ((m_stereo * 1152) < inSamples)
      inSamples = m_stereo * 1152;

    res = mp3iencGetFrame((Ipp16s *)in->GetDataPointer(), &bytes,
        (Ipp8u *)out->GetDataPointer(), state);

    if (res == MP3_OK) {
      pts_start = in->GetTime();

      if (pts_start < 0)
        pts_start = m_pts_prev;

      m_pts_prev = pts_end = pts_start+(Ipp32f)(inSamples/m_stereo)/(Ipp32f)(m_freq);

      out->SetTime(pts_start, pts_end);
      out->SetDataSize(bytes);
      in->MoveDataPointer(inSamples*sizeof(Ipp16s));
      in->SetTime(pts_end);

      m_frame_num++;
    }

    MemUnlock();
    return StatusMP3_2_UMC(res);
  }

  Status MP3EncoderInt::GetInfo(BaseCodecParams *info)
  {
    if (!info)
      return UMC_ERR_NULL_PTR;

    info->m_SuggestedInputSize = m_stereo * sizeof(Ipp16s) * 1152;
    //info->m_SuggestedOutputSize = 4096;
    info->m_SuggestedOutputSize = m_stereo * sizeof(Ipp16s) * 1152;
    if (m_layer == 3)
      info->m_SuggestedOutputSize += 1024;

    AudioCodecParams *pAudioCodecInfo =
      DynamicCast < AudioCodecParams, BaseCodecParams > (info);

    if (!pAudioCodecInfo)
      return UMC_OK;

    pAudioCodecInfo->m_info_in.bitPerSample = 16;
    pAudioCodecInfo->m_info_out.bitPerSample = 16;
    pAudioCodecInfo->m_info_in.bitrate = 0;
    pAudioCodecInfo->m_info_out.bitrate = m_bitrate;
    pAudioCodecInfo->m_info_in.channels = m_stereo;
    pAudioCodecInfo->m_info_out.channels = m_stereo;

    pAudioCodecInfo->m_info_in.sample_frequency = m_freq;
    pAudioCodecInfo->m_info_out.sample_frequency = m_freq;

    pAudioCodecInfo->m_info_in.stream_type = PCM_AUDIO;
    pAudioCodecInfo->m_info_out.stream_type = MP1L3_AUDIO;

    return UMC_OK;
  }

  Status MP3EncoderInt::GetDuration(Ipp32f *p_duration)
  {
    Ipp32f duration;

    if (!p_duration)
      return UMC_ERR_NULL_PTR;

    if (!m_initialized)
      return UMC_ERR_NOT_INITIALIZED;

    duration = (Ipp32f)m_frame_num *1152;

    duration /= (Ipp32f)(m_freq);

    p_duration[0] = duration;

    return UMC_OK;
  }

  Status MP3EncoderInt::SetParams(BaseCodecParams * /* params */) {
    return UMC_ERR_NOT_IMPLEMENTED;
  }

  Status MP3EncoderInt::Reset() {
    return UMC_ERR_NOT_IMPLEMENTED;
  }

  Status MP3EncoderInt::StatusMP3_2_UMC(MP3Status st)
  {
      Status res;

      if (st == MP3_OK)
          res = UMC_OK;
      else if (st == MP3_NOT_ENOUGH_DATA)
          res = UMC_ERR_NOT_ENOUGH_DATA;
      else if (st == MP3_BAD_FORMAT)
          res = UMC_ERR_INVALID_STREAM;
      else if (st == MP3_ALLOC)
          res = UMC_ERR_ALLOC;
      else if (st == MP3_BAD_STREAM)
          res = UMC_ERR_INVALID_STREAM;
      else if (st == MP3_NULL_PTR)
          res = UMC_ERR_NULL_PTR;
      else if (st == MP3_NOT_FIND_SYNCWORD)
          res = UMC_ERR_SYNC;
      else if (st == MP3_NOT_ENOUGH_BUFFER)
          res = UMC_ERR_NOT_ENOUGH_BUFFER;
      else if (st == MP3_UNSUPPORTED)
          res = UMC_ERR_UNSUPPORTED;
      else if (st == MP3_FAILED_TO_INITIALIZE)
          res = UMC_ERR_INIT;
      else
          res = UMC_ERR_UNSUPPORTED;

      return res;
  }

  Status MP3EncoderInt::MemLock() {
    MP3Enc *pOldState = state;

    if (!m_pMemoryAllocator) {
      vm_debug_trace(VM_DEBUG_ERROR, VM_STRING("External Lock failed: m_pMemoryAllocator is NULL\n"));
      return UMC_ERR_ALLOC;
    }

    state = (MP3Enc *)m_pMemoryAllocator->Lock(stateMemId);
    if(!state) {
      vm_debug_trace(VM_DEBUG_ERROR, VM_STRING("External Lock failed\n"));
      return UMC_ERR_ALLOC;
    }
    if (state != pOldState) {
      mp3iencUpdateMemMap(state, (Ipp8u *)state-(Ipp8u *)pOldState);
    }
    return UMC_OK;
  }

  Status MP3EncoderInt::MemUnlock() {
    if (stateMemId) {
      if (m_pMemoryAllocator->Unlock(stateMemId) != UMC_OK) {
        vm_debug_trace(VM_DEBUG_ERROR, VM_STRING("External Unlock failed\n"));
        return UMC_ERR_ALLOC;
      }
    }
    return UMC_OK;
  }
}      // namespace UMC

#endif //UMC_ENABLE_MP3_INT_AUDIO_ENCODER
